package com.cory.admin.sys.config;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.lang3.RandomStringUtils;

@WebServlet(urlPatterns = "/captchaImage")
public class CaptchaImageServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    private static final int charCount = 4;
    private static final int lineCount = 2;
    private static final int wordWidth = 20;
    private static final int wordHeight = 10;
    private static final int fontWeight = 24;

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {


        HttpSession session = req.getSession();
        resp.setHeader("Pragma", "no-cache");
        resp.setHeader("Cache-Control", "no-cache");
        resp.setDateHeader("Expires", 0L);
        resp.setContentType("image/jpeg");
        ServletOutputStream sos = resp.getOutputStream();
        try {
            RandomGraphic randomGraphic =
                    RandomGraphic.createInstance(charCount, lineCount, wordWidth, wordHeight, fontWeight);
            String code = randomGraphic.drawAlphaNumber(RandomGraphic.GRAPHIC_JPEG, sos);
            session.setAttribute(AdminConstants.SESSION_IMG_CODE, code);
            sos.flush();
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            sos.close();
        }
    }
    
}

/**
 * 生成随机数字或字母串，以图像方式显示，用于人工识别，使程序很难识别。 
 *  减小系统被程序自动攻击的可能性。
 *  生成的图形颜色由红、黑、蓝、紫4中随机组合而成，数字或字母垂直方向位置在
 *  一定范围内也是随机的，减少被程序自动识别的几率。
 *  由于数字的0，1，2易和字母的o，l,z混淆，使人眼难以识别，因此不生成数字
 *  和字母的混合串。
 *  生成的串字母统一用小写，串的最大长度为16。
 *
 */ 
class RandomGraphic {
    private static String RANDOM_SOURCE = "abcdefghijklmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ123456789";
    //颜色数组，绘制字串时随机选择一个
    private static  Color[] CHAR_COLOR = {new Color(79,174,189)};
    private static  Color[] LINE_COLOR = { Color.GRAY};
    
    //  字符的高度和宽度，单位为像素
    private int wordWidth = 20;
    private int wordHeight = 10;
    //字符大小
    private int fontWeight = 24;
    //最大字符串个数
    private  static final int MAX_CHARCOUNT = 16;
 
    //垂直方向起始位置
    private final int initypos = 8;
 
 
    //要生成的字符个数，由工厂方法得到
    private int charCount = 4;
    private int lineCount = 2;
 
    //随机数生成器
    private Random r = new Random();
 
    /**
     * 生成图像的格式常量，JPEG格式,生成为文件时扩展名为.jpg；
     * 输出到页面时需要设置MIME type 为image/jpeg
     */
    public static String GRAPHIC_JPEG = "JPEG";
    /**
     * 生成图像的格式常量，PNG格式,生成为文件时扩展名为.png；
     * 输出到页面时需要设置MIME type 为image/png
     */
    public static String GRAPHIC_PNG = "PNG";
 
 
 
    //用工厂方法创建对象
    protected RandomGraphic(int charCount){
        this.charCount = charCount;
    }
    
    //用工厂方法创建对象
    protected RandomGraphic(int charCount, int lineCount){
        this.charCount = charCount;
        this.lineCount = lineCount;
    }
 
    
    //用工厂方法创建对象
    protected RandomGraphic(int charCount, int lineCount,int wordWidth,int wordHeight,int fontWeight){
        this.charCount = charCount;
        this.lineCount = lineCount;
        this.wordWidth = wordWidth;
        this.wordHeight = wordHeight;
        this.fontWeight = fontWeight;
    }
    
 
    /**
     * 创建对象的工厂方法
     * @param charCount  要生成的字符个数，个数在1到16之间
     * 
     * Return 返回RandomGraphic对象实例
     * @throws Exception  参数charCount错误时抛出
     */
    public static RandomGraphic createInstance(int charCount) throws Exception{
        if (charCount < 1 || charCount > MAX_CHARCOUNT){
            throw new Exception("Invalid parameter charCount,charCount should between in 1 and 16");
        }
        return new RandomGraphic(charCount);
    }
    
    /**
     * 创建对象的工厂方法
     * @param charCount  要生成的字符个数，个数在1到16之间
     * 
     * Return 返回RandomGraphic对象实例
     * @throws Exception  参数charCount错误时抛出
     */
    public static RandomGraphic createInstance(int charCount, int lineCount) throws Exception{
        return new RandomGraphic(charCount, lineCount);
    }
    
    /**
     * 创建对象的工厂方法
     * @Title: createInstance 
     * @Description: TODO(这里用一句话描述这个方法的作用) 
     * @param @param charCount
     * @param @param lineCount
     * @param @param wordWidth
     * @param @param wordHeight
     * @param @param fontSize
     * @param @return    设定文件 
     * @return RandomGraphic    返回类型 
     * @throws
     */
    public static RandomGraphic createInstance(int charCount,int lineCount,int wordWidth,int wordHeight,int fontSize){
        return new RandomGraphic(charCount, lineCount, wordWidth, wordHeight, fontSize);
    }
    
    /**
     * 随机生成一个数字串，并以图像方式绘制，绘制结果输出到流out中
     *
     * @param graphicFormat 设置生成的图像格式，值为GRAPHIC_JPEG或GRAPHIC_PNG
     * @param out  图像结果输出流
     * @return   随机生成的串的值
     * @throws IOException 
     */
    public String drawNumber(String graphicFormat,OutputStream out) throws IOException{
        //    随机生成的串的值
        String charValue = "";
        charValue = randNumber();
        return draw(charValue,graphicFormat,out);    
    }

/**
    * 随机生成一个字母串，并以图像方式绘制，绘制结果输出到流out中
    * 
    * @param graphicFormat
    *            设置生成的图像格式，值为GRAPHIC_JPEG或GRAPHIC_PNG
    * @param out
    *            图像结果输出流
    * @return 随机生成的串的值
    * @throws IOException
    */
    public String drawAlpha(String graphicFormat, OutputStream out)
                throws IOException {
        // 随机生成的串的值
        String charValue = "";
        charValue = randAlpha();
        return draw(charValue, graphicFormat, out);
    } 
 
    /**
     * 随机生成一个字母数字混合串，并以图像方式绘制，绘制结果输出到流out中
     * @param graphicFormat
     * @param out
     * @return
     * @throws IOException
     */
    public String drawAlphaNumber(String graphicFormat, OutputStream out)
                throws IOException {
        // 随机生成的串的值
        String charValue = "";
        charValue = randAlphaNumber();
        return draw(charValue, graphicFormat, out);
    } 
    /**
     * 以图像方式绘制字符串，绘制结果输出到流out中
     * @param charValue  要绘制的字符串
     * @param graphicFormat 设置生成的图像格式，值为GRAPHIC_JPEG或GRAPHIC_PNG
     * @param out  图像结果输出流
     * @return   随机生成的串的值
     * @throws IOException 
     */
    protected String draw(String charValue,String graphicFormat,OutputStream out) throws IOException{
      
        //计算图像的宽度和高度
        int w = (charCount+2) * wordWidth;
        int h = wordHeight * 3;
        
        //创建内存图像区
        BufferedImage bi = new BufferedImage(w,h,BufferedImage.TYPE_3BYTE_BGR);
        Graphics2D g = bi.createGraphics();
          
        //设置背景色
        Color backColor = Color.WHITE;
        g.setBackground(backColor);
        g.fillRect(0,0,w,h);
        
        // 随机产生lineCount条干扰线，使图象中的认证码不易被其它程序探测到
        // 生成随机类
        Random random = new Random();
        for (int i = 0; i < lineCount; i++) {
            int y = random.nextInt(h);
            int yl = random.nextInt(h);
            Color color =  LINE_COLOR[randomInt(0,LINE_COLOR.length)];
            g.setColor(color);
            g.drawLine(0, y, w, yl);
        }
        
        //设置font
        g.setFont(new Font("Tahoma",Font.BOLD,fontWeight));
        //绘制charValue,每个字符颜色随机
        for(int i = 0; i < charCount; i++){
            String c = charValue.substring(i,i+1);
            Color color =  CHAR_COLOR[randomInt(0,CHAR_COLOR.length)];
            g.setColor(color);
            int xpos = (i+1) * wordWidth;
            //垂直方向上随机
            int ypos = randomInt(initypos+wordHeight,initypos+wordHeight*2);
            g.drawString(c,xpos,ypos);
        }
        
        g.dispose();
        bi.flush();
        // 输出到流
        ImageIO.write(bi,graphicFormat,out);
        
        return charValue;
    }

    protected String randNumber(){
        StringBuilder sb = new StringBuilder();
        //生成随机数字串
        for (int i = 0; i < charCount; i++){
            sb.append(String.valueOf(randomInt(0,10)));
        }
        return sb.toString();
    } 
 
    private String randAlpha(){
        StringBuilder sb = new StringBuilder();
        //生成随机字母串
        for (int i = 0; i < charCount; i++){
            char c = (char) (randomInt(0,26)+'A');
            sb.append(String.valueOf(c));
        }
        return sb.toString();
    } 
    
    /**
     * 随机字母与数字组合
     * @return
     */
    private String randAlphaNumber(){
        return RandomStringUtils.random(charCount,RANDOM_SOURCE);
        //return randomAlphanumeric(charCount).toUpperCase();
    } 
 
    /**
     * 返回[from,to)之间的一个随机整数
     * 
     * @param from 起始值
     * @param to 结束值
     * @return  [from,to)之间的一个随机整数
     */
    protected int randomInt(int from,int to){
        //Random r = new Random();
        return from+r.nextInt(to-from);
    }
    
    /**
     * 给定范围获得随机颜色
     * @param fc
     * @param bc
     * @return
     */
    public Color getRandColor(int fc, int bc){
        Random random = new Random();
        if (fc > 255){
          fc = 255;
        }
        if (bc > 255){
          bc = 255;
        }
        int r = fc + random.nextInt(bc - fc);
        int g = fc + random.nextInt(bc - fc);
        int b = fc + random.nextInt(bc - fc);
        return new Color(r, g, b);
    }
 
    
    public static void main(String[] args) throws Exception
    {
        long current = System.currentTimeMillis();
        for(int i=0;i<10;i++){
            RandomGraphic randomGraphic = RandomGraphic.createInstance(4);
            String code = randomGraphic.drawAlphaNumber(RandomGraphic.GRAPHIC_JPEG,new FileOutputStream(new File("e:/"+i+".jpeg")));  
            System.out.println(code);
        }
        System.out.println("1000，time:"+(System.currentTimeMillis()-current));
    }
}

